package com.accedo.achievements.dataaccess.dao.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.accedo.achievements.dataaccess.dao.CampaignDao;
import com.accedo.achievements.dataaccess.model.Campaign;

/**
 * A DAO class with campaign model database operations.
 * @author Bo Liang
 */
@Component
public class CampaignDaoImpl implements CampaignDao {
	
	private static final Logger log = LoggerFactory.getLogger(CampaignDaoImpl.class);
	
	private SessionFactory sessionFactory; //injection
	private Campaign camp; //injection
	
	//Constructor
	CampaignDaoImpl() {}; 
	
	/**
	 * Get available campaign object List.
	 * <p>only the campaign's end date is greater than current system date,
	 * this campaign is able to be added to the campaign list and return.
	 * @return List with campaign object
	 */
	@SuppressWarnings("unchecked")
	public List<Campaign> getAvailableCampList() {
		log.debug("Entering getAvailableCampList()");
		List<Campaign> campList = new ArrayList<Campaign>(); 
		Session session = null; 
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession();
			Query query = session.createQuery("from Campaign camp where camp.camp_end > :newDate")
						.setParameter("newDate", new Date()); 
			campList = query.list();  
			
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getAvailableCampList Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		log.debug("Leaving getAvailableCampList(): campList.size = {}", campList.size());
		return campList; 
	}
	
	/**
	 * Get the user's all active campaign objects in Map format by using user id.
	 * <p>the active campaign for user means that this user at least unlocked one achievement related this campaign.
	 * this function return two types of Map key-value pair: if one campaign's achievements have already totally unlocked by user, it puts <camp_id, "finished">,
	 * if one campaign's achievements have not unlocked completed but at least one, it puts <camp_id, "playing"> into Map.
	 * @param user_id	user id
	 * @return			List of campaign id
	 */
	@SuppressWarnings("unchecked")
	public Map<Integer, String> getActiveCampIdMapByUserId(int user_id) {
		log.debug("Entering getActiveCampIdMapByUserId(user_id = {})", user_id);
		
		// the campaign id list that stored the campaign which user have already obtained
		List<Integer> unlockedCampIdList = new ArrayList<Integer>();
		
		// the campaign id list that stored the campaign which user unlocked at least one achievement
		List<Integer> campIdList = new ArrayList<Integer>();
		
		Map<Integer, String> returnMap = new HashMap<Integer, String>();
		int camp_id; 
		Session session = null; 
		
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession();
			
			// unlock_flag equals 2 means this user obtained this campaign's award
			Query query = session.createSQLQuery("select camp_id from t_user_campaign where user_id = :user_id and unlock_flag = 2")
					.setParameter("user_id", user_id); 
			unlockedCampIdList = query.list(); 
			
			query = session.createSQLQuery("select camp_id from t_user_campaign where user_id = :user_id")
					.setParameter("user_id", user_id); 
			campIdList = query.list(); 
			
			for (Iterator<Integer> iter = campIdList.iterator(); iter.hasNext(); ) {
				camp_id = iter.next(); 
				if (unlockedCampIdList.contains(camp_id)) {
					// if this campaign is obtained, put "finished" in the key-value pair
					returnMap.put(camp_id, "finished");
				} else {
					/**
					 *  if this badge has at least one achievement unlocked,
					 *  or this user has totally unlocked all of achievements,
					 *  but have not been given the award of this campaign, 
					 *  put "playing" in the key-value pair
					 */
					returnMap.put(camp_id, "playing");
				}
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getActiveCampIdMapByUserId Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		
		log.debug("Leaving getActiveCampIdMapByUserId(): returnMap.size = {}", returnMap.size());
		return returnMap; 
	}
	
	
	/**
	 * Get campaign id List by using achievement id.
	 * <p> one achievement may related several campaigns,
	 * it means one campaign includes several achievements,
	 * if you want to join the campaign, you have to unlock all of the relevant achievements 
	 * and several campaigns may have one same achievement,
	 * this function returns these campaigns id which have this same achievement,
	 * return these badge's id in a List.
	 * 
	 * @param achi_id	achievement id
	 * @return			List of campaign's id
	 */
	@SuppressWarnings("unchecked")
	public List<Integer> getCampIdListByAchiId(int achi_id) {
		log.debug("Entering getCampIdListByAchiId(achi_id = {})", achi_id);
		List<Integer> campIdList = new ArrayList<Integer>();
		Session session = null;
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession();
			Query query = session.createSQLQuery("select camp_id from t_campaign_achievement where achi_id = :achi_id")
					.setParameter("achi_id", achi_id); 
			campIdList = query.list(); 
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getCampIdListByAchiId Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		log.debug("Leaving getCampIdListByAchiId(): campIdList.size = {}", campIdList.size());
		return campIdList; 
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Campaign getCamp() {
		return camp;
	}

	@Resource
	public void setCamp(Campaign camp) {
		this.camp = camp;
	}
	

}
